package ru.volgogradetzzz.utils 
{
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.KeyboardEvent;
	import flash.text.TextField;
	import flash.text.TextFieldType;
	import flash.text.TextFormat;
	import flash.text.TextFormatAlign;
	import flash.utils.Dictionary;
	
	/**
	 * ...
	 * @author Volgogradetzzz
	 */
	public class Console extends Sprite 
	{
		private const _LINE_NUMBER_WIDTH:uint = 30;
		private const _TF_LINE_NUMBER:TextField = new TextField();
		private const _TF:TextField = new TextField();
		private const _TF_INPUT:TextField = new TextField();
		private const _COMMANDS_BY_NAME:Dictionary = new Dictionary();
		private const _HELPS_BY_NAME:Dictionary = new Dictionary();
		private const _COMMAND_NAME_AND_PARAMETERS_PATTERN:RegExp = /([-\w\d]*)( +(.*))?/;
		private const _NO_WHITESPACE_PATTERN:RegExp = / *([^ ]*) */;
		
		private static var instance:Console;
		
		private var _h:uint = 100;
		private var _lineNumberBg:Shape = new Shape();
		private var _bg:Shape = new Shape();
		
		public static function getInstance():Console 
		{
			if (!instance)
			{
				instance = new Console(SINGLETON_KEY);
			}
			
			return instance;
		}
		
		public function Console(checkKey:Object) 
		{
			if (checkKey != SINGLETON_KEY)
			{
				throw(new Error("Экземпляр Console необходимо получать с помощью метода Console.getInstance()."));
			}
			
			addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
			
			_lineNumberBg.graphics.beginFill(0x000000, 0.8);
			_lineNumberBg.graphics.drawRect(0, 0, _LINE_NUMBER_WIDTH, _h);
			_lineNumberBg.graphics.endFill();
			
			_bg.graphics.beginFill(0x000000, 0.6);
			_bg.graphics.drawRect(0, 0, 100, _h);
			_bg.graphics.endFill();
			_bg.x = _LINE_NUMBER_WIDTH;
			
			_TF_LINE_NUMBER.multiline = true;
			_TF_LINE_NUMBER.wordWrap = true;
			_TF_LINE_NUMBER.width = _LINE_NUMBER_WIDTH;
			_TF_LINE_NUMBER.defaultTextFormat = new TextFormat('Arial', 10, 0xC0C0C0, null, null, null, null, null, TextFormatAlign.RIGHT);
			_TF_LINE_NUMBER.selectable = false;
			
			_TF.multiline = true;
			_TF.wordWrap = true;
			_TF.defaultTextFormat = new TextFormat('Arial', 10, 0xFFFFFF);
			_TF.x = _LINE_NUMBER_WIDTH;
			_TF.addEventListener(Event.SCROLL, textScrollHandler);
			
			_TF_INPUT.height = 20;
			_TF_INPUT.multiline = false;
			_TF_INPUT.wordWrap = false;
			_TF_INPUT.type = TextFieldType.INPUT;
			_TF_INPUT.defaultTextFormat = new TextFormat('Arial', 10, 0xFFFF00);
			_TF_INPUT.x = _LINE_NUMBER_WIDTH;
			_TF_INPUT.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
			_TF_INPUT.background = true;
			_TF_INPUT.backgroundColor = 0x505050;
			
			addChild(_lineNumberBg);
			addChild(_bg);
			addChild(_TF_LINE_NUMBER);
			addChild(_TF);
			addChild(_TF_INPUT);
			
			registerCommand(showHelp, '-help', 'Вызов без аргументов - выведет список всех команд и их применение, с аргументом commandName - применения выбранной команды');
			registerCommand(changeHeight, '-height', 'h - высота косоли');
		}
		
		public function registerCommand(command:Function, commandName:String, help:String = null):void
		{
			_COMMANDS_BY_NAME[commandName] = command;
			_HELPS_BY_NAME[commandName] = help;
		}
		
		private function showHelp(commadName:String = null):void
		{
			if (commadName)
			{
				_TF.htmlText += '<font face="Arial" size="10" color="#FF00FF">' + commadName + '</font>';
				if (_HELPS_BY_NAME[commadName])
				{
					_TF.htmlText += '<font face="Arial" size="10" color="#0000FF">\t' + _HELPS_BY_NAME[commadName] + '</font>';
				}
			}
			else
			{
				var arr:Array = new Array();
				for (var name:String in _COMMANDS_BY_NAME) 
				{
					arr.push(name);
				}
				arr.sort();
				for (var i:int = 0; i < arr.length; i++) 
				{
					_TF.htmlText += '<font face="Arial" size="10" color="#FF00FF">' + arr[i] + '</font>';
					if (_HELPS_BY_NAME[arr[i]])
					{
						_TF.htmlText += '<font face="Arial" size="10" color="#0000FF">\t' + _HELPS_BY_NAME[arr[i]] + '</font>';
					}
				}
			}
		}
		
		private function changeHeight(h:uint):void
		{
			_h = h;
			updateHeight();
		}
		
		private function textScrollHandler(event:Event):void 
		{
			_TF_LINE_NUMBER.scrollV = _TF.scrollV;
		}
		
		private function keyDownHandler(event:KeyboardEvent):void 
		{
			if (event.keyCode == 13)
			{
				_TF.htmlText += '<font face="Arial" size="10" color="#FFFFFF">' + _TF_INPUT.text + '</font>';
				
				var commandName:String = getCommadName(_TF_INPUT.text);
				if (_COMMANDS_BY_NAME[commandName])
				{
					var params:Array = getParameters(_TF_INPUT.text);
					
					try
					{
						_COMMANDS_BY_NAME[commandName].apply(this, params);
						_TF.htmlText += '<font face="Arial" size="10" color="#00FF00">Команда выполнена</font>';
					}
					catch (error:Error)
					{
						if (error.errorID == 1063)
						{
							_TF.htmlText += '<font face="Arial" size="10" color="#FF0000">Неправильное количество аргументов</font>';
						}
						else
						{
							trace(error.message);
						}
					}
				}
				else
				{
					_TF.htmlText += '<font face="Arial" size="10" color="#FF0000"> Команда не зарегистрирована</font>';
				}
				
				_TF.scrollV = _TF.numLines;
				
				_TF_LINE_NUMBER.text = '';
				for (var i:int = 1; i <= _TF.numLines; i++) 
				{
					_TF_LINE_NUMBER.appendText(i + '\n');
				}
				
				_TF_LINE_NUMBER.scrollV = _TF.scrollV;
			}
		}
		
		private function getCommadName(str:String):String
		{
			var arr:Array = _COMMAND_NAME_AND_PARAMETERS_PATTERN.exec(str);
			if (arr && arr.length > 2 && arr[1].length > 0) return arr[1];
			
			return null;
		}
		
		private function getParameters(str:String):Array
		{
			var arr:Array = _COMMAND_NAME_AND_PARAMETERS_PATTERN.exec(str);
			var params:Array;
			if (arr && arr.length > 0 && arr[3])
			{
				params = arr[3].split(',');
			}
			else
			{
				return [];
			}
			
			arr.length = 0;
			var temp:Array;
			for (var i:uint = 0; i < params.length; i++)
			{
				temp = _NO_WHITESPACE_PATTERN.exec(params[i]);
				if (temp && temp.length > 1) arr.push(temp[1]);
			}
			
			return arr;
		}
		
		private function addedToStageHandler(event:Event):void 
		{
			_bg.width = stage.stageWidth - _LINE_NUMBER_WIDTH;
			_TF.width = _bg.width;
			_TF_INPUT.width = _bg.width;
			
			updateHeight();
		}
		
		private function updateHeight():void 
		{
			_lineNumberBg.height = _h;
			_bg.height = _h;
			
			_TF.height = _h - _TF_INPUT.height;
			_TF_LINE_NUMBER.height = _h - _TF_INPUT.height;
			_TF_INPUT.y = _h - _TF_INPUT.height;
		}
	}
}

internal const SINGLETON_KEY:Object = {};